/////////////////////
// SOUND FUNCTIONS //
/////////////////////

// ------------- Send sound data -----------------------------------
void LivingSpot::sendSoundData(void) {
 // here, use the current spot info to generate a midi note, send OCS or whatever. 
 // Relevant data is:
	/*
	int identifier; // spot number (could be used as the index for a table containing the channel/instrument to be played)
	float maxI_Volts;// = 0.0; // note: maxI_Volts and minI_Volts are defined as public variables now (easy access as relevant spot information). 
	float minI_Volts;// = 1024.0;
	// note: position is private because we want to avoid out of range values - but we may need to get their value for sound generation
	float depth_Range; // this is the z coordinate: not used yet...
	float vx, vy; //current speed
	float ax, ay; // current acceleration
	float targetAngle;// the direction of the recentering vector
	boolean touchWall; // detection of wall borders
	boolean collision;// detection of collision with other particules
	*/
	
	// Also, activate green laser when bouncing or collision:
        //if (collision||touchWall) {
	//...
	
	//}
}

// Midi sound generation (equivalent to sendSoundData in the future): 
void midiSoundGeneration(LivingSpot* spot) {
	switch (spot->behaviourMode) {
		case STAND_CENTER:
                        // to do
			break;
                case DEPTH_LETTERS:
                       // to do 
                break;
                case TRACKING:
                      // to do 
                break;
		case SIMPLE_BOUNCING:
		    //	to do
                    // REM: bouncing produces collisions and wall contacts as with other modes, and will be taken care at the end of the function 
			break;
		case FAST_BOUNCING:
			// to do
			break;
		case NEWTON_DYNAMICS:
			// to do
			break;
		case COUNTOUR_FOLLOWING:
                        // SPECIAL SOUND that depends on the angle: 
                        if (spot->countourSoundMode==SUPER_BASS) {
                          float dist = sqrt(sq(CENTER_AD_MIRROR_X - spot->getAz()) + sq(CENTER_AD_MIRROR_Y - spot->getEl()));
                          midiFollowingSuperBass2(spot->onContour, spot->target_angle, dist);  
                        } else 
                        {
                           float dist = sqrt(sq(CENTER_AD_MIRROR_X - spot->getAz()) + sq(CENTER_AD_MIRROR_Y - spot->getEl()));
                           //midiFollowingFreeBass2(spot->onContour, spot->target_angle, dist); // NOT DONE!!!
                            midiFollowingSuperBass2(spot->onContour, spot->target_angle, dist);  
                        }
			break;
		case SNELL_LAW:
			// to do
			break;
		default:
			break;
		}
                
                
		// Common action for all modes:
		if (spot->collision) {
                       
                        // SOUND: 
                        if (spot->collisionSoundMode==SUPER_BASS)
                            midiBouncingSuperBass();
                        else 
                            midiBouncingFreeBass2();
		}

		if (spot->touchWall) {     
			
                        // SOUND: 
                        if (spot->touchSoundMode==SUPER_BASS)
                            midiBouncingSuperBass();
                        else 
                            midiBouncingFreeBass2();
		}        
}



// =====================================   MIDI   ==================================================== 
#define PI2 (2*PI)
#define PI3sur2 4.71

byte previousNote[NBSPOT]; // No more than 4 spots!!
byte followingNote;
byte bouncingNote0;
byte bouncingNote1;
unsigned long lastCollision0;
unsigned long lastCollision1;

#define nbAngleAvr 5
float angleTab[nbAngleAvr];
int idAngleAvr = 0;

float lastAngle;
unsigned long lastMidiTime;

//initialization of the midi device
//assigned instruments to used chanels
void midiInit(){
  
  // Setup the tiny midi chip: 
  // NOTE: we need to put high on the chip select pin (before it was done using an arduino pin, now is wired). 
   
  // setup midi communication (speed = 31250)
  Serial1.begin(31250);
   
  //setup instruments to chanels [0-15] ('9' is always drum)
  //midiProgramChange (1, );
 
  /*
  midiProgramChange (0, 13); //collision chanel - Spot 0
  midiProgramChange (1, 10); //collision chanel - Spot 1
  midiProgramChange (2, 11); //collision chanel - Spot 2
  midiProgramChange (3, 12); //collision chanel - Spot 3
   
  midiProgramChange (4, 39); //pitch bend chanel - Spot 0
  midiProgramChange (5, 43); //pitch bend chanel - Spot 1
  midiProgramChange (6, 41); //pitch bend chanel - Spot 2
  midiProgramChange (7, 42); //pitch bend chanel - Spot 3
  */
  
  for(int i=0; i<NBSPOT; i++){
    previousNote[i]=128;
  }
  followingNote = 128;
 
  lastMidiTime = millis();
  
}

///////////////////////////////////////////////////////////

void midiTouchWall(int i){
  midiNoteOn(9, 38 + i, 127);
}

void midiCollision(int i, float angle){
  byte note = byte((70.0 * angle / 6.283) + 30);
  midiNoteOn(i, note, 127);
}


////////////synthe Luc//////////////////

void midiBouncingFreeBass1(){
  //24 is minimum and 72 is maximum for superBass and for freeBass
  float deltaCollision = millis() - lastCollision0;
  if(deltaCollision > 700.0) deltaCollision = 700.0;
  byte note = byte(36 - (12.0 *  sqrt(deltaCollision) / 26.5));
  note /= 2;
  note *= 2;
  midiNoteOff(0, bouncingNote0);
  midiNoteOn(0, note, 115);
  bouncingNote0 = note;
  lastCollision0 = millis();
}

//une seule note
void midiBouncingFreeBass2(){
  //24 is minimum and 72 is maximum for superBass and for freeBass
  midiNoteOff(0, 30);
  midiNoteOn(0, 30, 100);
}

void midiBouncingSuperBass(){
  //24 is minimum and 72 is maximum for superBass and for freeBass
  float deltaCollision = millis() - lastCollision1;
  if(deltaCollision > 700.0) deltaCollision = 700.0;
  byte note = byte(48 - (24.0 *  sqrt(deltaCollision) / 26.5));
  note /= 2;
  note *= 2;
  midiNoteOff(1, bouncingNote1);
  midiNoteOn(1, note, 100);
  bouncingNote1 = note;
  lastCollision1 = millis();
  
  //midi "scheduler" for control change
  if(millis() > lastMidiTime + 100){
    
    //cutoff [0..110]; rezo [10..120]; decay[0..100]
    byte cutoff = 110 * (sin(5 * (millis()-schedulerTime) / 50000.0 + PI3sur2) +1 ) / 2;
    byte rezo = 110 * (sin(3 * (millis()-schedulerTime) / 50000.0 + PI3sur2 ) +1 ) / 2 +10;
    byte decay = 100 * (sin(1 * (millis()-schedulerTime) / 50000.0 + PI3sur2) +1 ) / 2;
    byte enveloppe = 40 * (sin(0.4 * (millis()-schedulerTime) / 50000.0 + PI3sur2) +1 ) / 2 +87;
    
    midiControlChange(1, 107, cutoff);
    midiControlChange(1, 106, rezo);
    midiControlChange(1, 102, decay);
    midiControlChange(1, 104, enveloppe);
    
    
    spotArray[0].setRadiusSaccade(60 * cutoff / 110 + 30);
    
    lastMidiTime = millis();
  }
}


//normal controle pitch
//distance au centre controle cutoff
//LFO pour enveloppe
//mettre decay a 64
void midiFollowingSuperBass1(boolean active, float angle, float distance){
  if(active){
    
    if(angle > PI){ angle = PI2 - angle;}
    angleTab[idAngleAvr] = angle;
    idAngleAvr++;
    if(idAngleAvr == nbAngleAvr){
      float angleAvr = 0;
      idAngleAvr = 0;
      for(int i=0; i<nbAngleAvr; i++){
        angleAvr += angleTab[i];
      }
      angleAvr /= nbAngleAvr;
      
      byte note = (byte)(24 + (36 * angleAvr / PI) );
      note /= 2;
      note *= 2;
      midiNoteOff(1, followingNote);
      midiNoteOn(1, note, 100);
      followingNote = note;
      
      byte cutOff = (byte)(110 - (100 * distance / 2120));
      midiControlChange(1, 107, cutOff);
      
    }    
    lastAngle = angle;
  }
  
  
  //midi "scheduler" for the continus note
  if(millis() > lastMidiTime + 200){
    //midiNoteOff(1, 30);
    //midiNoteOn(1, 30, 100);
    
    byte rezo = 100 * (sin(2 * (millis()-schedulerTime) / 50000.0 + PI3sur2 ) +1 ) / 2 + 27;
    byte enveloppe = 127 * (sin(3 * (millis()-schedulerTime) / 50000.0 + PI3sur2) +1 ) / 2;
    byte decay = 100 * (sin(1 * (millis()-schedulerTime) / 50000.0 + PI3sur2) +1 ) / 2 + 27;
    
    midiControlChange(1, 106, rezo);
    midiControlChange(1, 104, enveloppe);
    midiControlChange(1, 102, decay);
    
    lastMidiTime = millis();
  }
  
}


void midiFollowingSuperBass2(boolean active, float angle, float distance){
  if(active){
    
    if(angle > PI){ angle = PI2 - angle;}
    angleTab[idAngleAvr] = angle;
    idAngleAvr++;
    if(idAngleAvr == nbAngleAvr){
      float angleAvr = 0;
      idAngleAvr = 0;
      for(int i=0; i<nbAngleAvr; i++){
        angleAvr += angleTab[i];
      }
      angleAvr /= nbAngleAvr;
      
      byte cutOff = (byte)(127 - (127 * distance / 2120));
      midiControlChange(1, 107, cutOff);
      
    }
    
    lastAngle = angle;
  }
  
  
  //midi "scheduler" for the continus note
  if(millis() > lastMidiTime + 220){
    // lastMidiTime + ??ms define tempo periode
    midiNoteOff(1, 30);
    midiNoteOn(1, 30, 100);
    
    byte rezo = 100 * (sin(2 * (millis()-schedulerTime) / 50000.0 + PI3sur2 ) +1 ) / 2 + 27;
    byte enveloppe = 127 * (sin(3 * (millis()-schedulerTime) / 50000.0 + PI3sur2) +1 ) / 2;
    byte decay = 100 * (sin(1 * (millis()-schedulerTime) / 50000.0 + PI3sur2) +1 ) / 2 + 27;
    
    midiControlChange(1, 106, rezo);
    midiControlChange(1, 104, enveloppe);
    midiControlChange(1, 102, decay);
    
    lastMidiTime = millis();
  }
  
}



////////////tiny midi board//////////////////

void midiPitchAngle(int i, boolean active, float angle){
  if(active){
    byte note = (byte)((100.0 * angle / 6.283));
    if(previousNote[i] <  128){
      note = (note + previousNote[i])  >> 1; 
    }
    midiNoteOff(1, previousNote[i]);
    midiNoteOn(1, note, 100);
    previousNote[i] = note;
  }
  else{
    if(previousNote[i] <  128){
      midiNoteOff(1, previousNote[i]);
      previousNote[i] = 128;
    }
  }
}

void midiPitchBendAngle(int i, boolean active, float angle){
  if(active){
    midiNoteOn(4+i, 50 + 10*i, 120);
    
    Serial1.print(0xE4+i, BYTE);
    Serial1.print(0x0, BYTE);
    Serial1.print(byte((126.0 * angle / 6.283)), BYTE);
  }
  else{
    midiNoteOff(4+i, 50 + 10*i);
  }
}

///////////////////////////////////////////////////////////

void midiAllSoundOff(){
  for(byte i=0; i<16; i++){
    Serial1.print(176 + i, BYTE);
    Serial1.print(120, BYTE);
    Serial1.print(0x0, BYTE);
  }
}

void midiAllNoteOff(){
  for(byte i=0; i<16; i++){
    Serial1.print(176 + i, BYTE);
    Serial1.print(123, BYTE);
    Serial1.print(0x0, BYTE);
  }
}

void midiNoteOn (char canal, char nota, char velocity) {
  Serial1.print(0x90 + canal, BYTE);
  Serial1.print(nota, BYTE);
  Serial1.print(velocity, BYTE);
}

void midiNoteOff (char canal, char nota) {
  Serial1.print(0x80 + canal, BYTE);
  Serial1.print(nota, BYTE);
  Serial1.print(0, BYTE);
}

void midiProgramChange (char canal, char programa) {
  Serial1.print(0xC0 + canal, BYTE);
  Serial1.print(programa, BYTE);
}

void midiControlChange(byte channel, byte control, byte value){
  Serial1.print(0xB0 + channel, BYTE);
  Serial1.print(control, BYTE);
  Serial1.print(value, BYTE);
}

void midiPitchBend (char canal, int bend) {
  Serial1.print(0xE0 + canal, BYTE);
  Serial1.print((bend + 8192) % 128, BYTE);
  Serial1.print((bend + 8192) / 128, BYTE);
}


